package com.ikas.ai.server.error;

import com.ikas.ai.BaseException;
import com.ikas.ai.handle.JsonResult;
import com.ikas.ai.handle.JsonResultType;
import com.ikas.ai.utils.MessageUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 能拦截抛异常、参数类型异常等情况，404无法拦截
 */
@SuppressWarnings("all")
@RestControllerAdvice
public class GlobalExceptionHandler {
    private static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    private static final String DEFAULT_ERROR_VIEW = "error";

    /*出现异常，返回的状态码500*/
    @ExceptionHandler(Exception.class)
    public String exceptionHandler(HttpServletRequest req, Exception e) {
        logger.info("捕获到异常信息...");
        if (e instanceof MethodArgumentNotValidException) {
            MethodArgumentNotValidException exception = (MethodArgumentNotValidException) e;
            return handlerMethodArgumentNotValidException(exception);
        } else if (e instanceof UnauthorizedException) {
            return handle401(req, (UnauthorizedException) e);
        } else if (e instanceof NoHandlerFoundException) {
            return handleNoHandlerFoundException((NoHandlerFoundException) e);
        } else if (e instanceof DataIntegrityViolationException) {
            return handleMysqlColumnTooLong(req, e);
        } else if (e instanceof SQLException) {
            return sqlExcetpion((SQLException) e);
        } else if (e instanceof BusinessException) {
            return businessException((BusinessException) e);
        } else {
            return errorHandler(req, e);
        }
    }

    public String businessException(BusinessException e) {
        logger.error(String.format("(BusinessException),exception: %s", e.getMessage()));
        String result = JsonResult.error(JsonResultType.INTERNALSERVERERROR, e.getMessage()).toJSON();
        return result;
    }

    public String sqlExcetpion(SQLException e) {
        logger.error(String.format("(SQLException),exception: %s", e.getMessage()));
        String result = JsonResult.error(JsonResultType.INTERNALSERVERERROR, "系统内部错误,请联系管理员").toJSON();
        return result;
    }

    private String handleMysqlColumnTooLong(HttpServletRequest req, Exception e) {
        logger.error(String.format("(handleMysqlColumnTooLong),exception: %s", e.getMessage()));
        String result = JsonResult.error(JsonResultType.BADREQUEST, "输入长度过长").toJSON();
        return result;
    }
    private String errorHandler(HttpServletRequest req, Exception e) {
        if (e instanceof BaseException) {
            logger.error("Request {} throw exception: {}\n\t{}", req.getRequestURI(), e.getMessage(), e.getStackTrace()[0]);
        } else {
            logger.error(String.format("Request %s throw exception: %s", req.getRequestURI(), e.getMessage()), e);
        }

        JsonResult result = new JsonResult(JsonResultType.INTERNALSERVERERROR);
        String msg = new String();
        if (e != null && StringUtils.isNotBlank(e.getMessage())) {
            msg = e.getMessage();
        }
        if (StringUtils.isBlank(msg) && e.getCause() != null && StringUtils.isNotBlank(e.getCause().getMessage())) {
            msg = e.getCause().getMessage();
        }
        if (StringUtils.isEmpty(msg)) {
            msg = "服务器内部异常";
        }
        if (msg.length() > 201) {
            result.setMessage(msg.substring(0, 200));
        } else {
            result.setMessage(msg);
        }
        return result.toJSON();
    }

    /**
     * @param e
     * @return
     */
    public String handle401(HttpServletRequest req, UnauthorizedException e) {
        logger.error(String.format("无权访问(Unauthorized),exception: %s", e.getMessage()));
        String message = e.getMessage();
        if (message.indexOf("[") != -1 && message.indexOf("]") != -1) {
            message = message.substring(message.indexOf("[") + 1, message.lastIndexOf("]"));
        }
        String result = JsonResult.error(JsonResultType.UNAUTHORIZED, "无权访问(Unauthorized):用户没有权限(" + message + ")").toJSON();
        return result;
    }

    /**
     * 捕捉404异常
     *
     * @return
     */
    public String handleNoHandlerFoundException(NoHandlerFoundException e) {
        return JsonResult.error(JsonResultType.NOTFOUND, "访问路径出错," + e.getMessage()).toJSON();
    }

    /**
     * 方法参数验证异常处理
     *
     * @param e 方法参数异常
     * @return jsonResult
     */
    public String handlerMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        JsonResult jsonResult = JsonResult.error(JsonResultType.BADREQUEST);
        List<String> errors = e.getBindingResult().getFieldErrors().stream().map(fieldError -> {
            String messageKey = fieldError.getDefaultMessage();
            return getI18nMessage(messageKey);
        }).collect(Collectors.toList());
        jsonResult.setMessage(StringUtils.join(errors, ","));
        return jsonResult.toJSON();
    }

    /**
     * 重复记录异常捕获
     *
     * @param e
     * @return
     */
    @ExceptionHandler(DuplicateKeyException.class)
    public String handleDuplicateKeyException(DuplicateKeyException e) {
        logger.error("数据库中已存在该记录," + e.getMessage(), e);
        return JsonResult.error("数据库中已存在该记录").toJSON();
    }

    /**
     * 处理所有参数校验时抛出的异常
     *
     * @param ex 校验异常
     * @return jsonResult
     */
    @ExceptionHandler(value = ValidationException.class)
    public String handleBindException(ValidationException e) {
        String msg = "";
        // 获取所有异常
        List<String> errors = new LinkedList<>();
        JsonResult jsonResult = JsonResult.error(JsonResultType.BADREQUEST);
        if (e instanceof ConstraintViolationException) {
            ConstraintViolationException exs = (ConstraintViolationException) e;
            Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
            for (ConstraintViolation<?> item : violations) {
                String message = item.getMessage();
                errors.add(message);
            }
            msg = StringUtils.join(errors, ",");
        } else {
            msg = e.getMessage();
        }
        logger.error(msg);
        jsonResult.setMessage(msg);
        return jsonResult.toJSON();
    }

    /**
     * 处理绑定异常
     *
     * @param e 绑定异常
     * @return jsonResult
     */
    @ExceptionHandler(value = BindException.class)
    public JsonResult handleBindException(BindException e) {
        BindingResult bindingResult = e.getBindingResult();
        // 获取所有异常
        List<String> errors = new LinkedList<String>();
        JsonResult jsonResult = JsonResult.error(JsonResultType.BADREQUEST);
        List<ObjectError> allErrors = bindingResult.getAllErrors();
        if (CollectionUtils.isEmpty(allErrors)) {
            jsonResult.setMessage("参数异常");
        } else {
            for (ObjectError error : allErrors) {
                StringBuilder msg = new StringBuilder();
                if (error instanceof FieldError) {
                    msg.append(((FieldError) error).getField()).append(": ");
                }
                String messageKey = error.getDefaultMessage() == null ? "" : error.getDefaultMessage();
                msg.append(getI18nMessage(messageKey));
                errors.add(msg.toString());
            }
        }
        jsonResult.setMessage(StringUtils.join(errors, ","));
        return jsonResult;
    }

    private String getI18nMessage(String messageKey) {
        String message = MessageUtils.get(messageKey);
        if (StringUtils.isEmpty(message)) {
            return messageKey;
        }
        return message;
    }

    /**
     * IllegalArgumentException异常捕获
     *
     * @param e
     * @return
     */
    @ExceptionHandler(value = {IllegalArgumentException.class})
    public String handleIllegalArgumentException(IllegalArgumentException e) {
        // 获取所有异常
        String message = "参数不合法，请检查";
        JsonResult jsonResult = JsonResult.error(JsonResultType.INVALIDPARAM);
        String error = e.getMessage();
        if (org.apache.commons.lang3.StringUtils.isBlank(error)) {
            jsonResult.setMessage(message);
        } else {
//            message = error;
            message = message + "：" + error;
        }
        logger.error(message);
        jsonResult.setMessage(message);
        return jsonResult.toJSON();
    }

}
